#ifndef SCENE_OBJCT_H
#define SCENE_IBJECT_H
#include  "stdafx.h"
#include "Maths.h"
#include "KdTree.h"
#include <vector>
#include "MeshesParser.h"


template <typename T>
class SceneObject
{
public:
	Vec3<T> center;
	Vec3<T> surfaceColor, emissionColor;    /// surface color and emission (light)
	T transparency, reflection, radius;    
	int objectType;
	string name;
	bool isLight;

	SceneObject(int objectType)
	{
		this->objectType = objectType;
	}

	//position, radius, surface color, reflectivity, transparency|, emission color
	SceneObject(Vec3<T> center,T radius, Vec3<T> surfaceColor,T reflection, T transparency ,Vec3<T> emissonColor,  int type)
	{
		this->isLight = true;
		this->center = center;
		this->surfaceColor = surfaceColor;
		this->emissionColor = emissonColor;
		this->reflection = reflection;
		this->objectType = 0;
		this->radius = radius;
		this->transparency = transparency;
		vector<Triangle<T>*> triangles;
	}

	void SetEmissionColor(Vec3<T> colorVector)
	{
		this->emissionColor = colorVector;
	}

	void SetName(string name)
	{
		this->name = name;
	}

	void SetReflection(T reflection)
	{
		this->reflection = reflection;
	}

	void SetSurfaceColor(Vec3<T> surfaceColor)
	{
		this->surfaceColor = surfaceColor;
	}

	void SetTransparency(T transparency)
	{
		this->transparency = transparency;
	}

	void SetCenter(Vec3<T> center)
	{
		this->center = center;
	}
	
	void SetEmissionLight(bool setLight)
	{
		this->emissionColor = setLight? Vec3<T>(1):Vec3<T>(0);
	}

	/*void SetEmissionColor(Vec3<T> colorVector)
	{
		this->emissionColor = colorVector;
	}

	void SetName(string name)
	{
		this->name = name;
	}

	void SetReflection(T reflection)
	{
		this->reflection = reflection;
	}

	void SetSurfaceColor(Vec3<T> surfaceColor)
	{
		this->surfaceColor = surfaceColor;
	}

	void SetTransparency(T transparency)
	{
		this->transparency = transparency;
	}

	void SetCenter(Vec3<T> center)
	{
		this->center = center;
	}*/

	/*
	The mesh normal getter is not present here but tackiling it in cod efound in the trace function
	*/
	virtual Vec3<T> GetIntersectionPointNormal(Vec3<T> &rayorig, Vec3<T> &raydir, T tnear = 0) = 0;


	virtual bool GetRayIntersection(Vec3<T> &rayorig, Vec3<T> &raydir, Vec3<T> interPoint) = 0;

	virtual T GetIntersectionDistance(Vec3<T> &rayorig, Vec3<T> &raydir, Vec3<T> intersectionPoint) = 0;

	//Sphere(const Vec3<T> &c, const T &r, const Vec3<T> &sc, const T &refl = 0, const T &transp = 0, const Vec3<T> &ec = 0)
	virtual void CreateSceneObject() = 0;


	
};

template <typename T>
class GenericObject
{
public:
	Vec3<T> center;
	Vec3<T> surfaceColor, emissionColor;    /// surface color and emission (light)
	T transparency, reflection, radius;    
	KdTree<T> * objectRep;
	int objectType;
	Sphere<T> *representedSphere;
	string name;
	T diffuse;
	bool isLight;
	
	GenericObject()
	{
		this->isLight = false;
		this->center = Vec3<T>(0);
		this->surfaceColor = Vec3<T>(0);
		this->emissionColor = Vec3<T>(0);
		this->reflection = 0;
		this->radius = 0;
		this->transparency = 0;
		this->diffuse = 0;
	}


	GenericObject(Vec3<T> center, Vec3<T> surfaceColor, Vec3<T> emissonColor,T transparency , T reflection, string filePath)
	{
		this->isLight = false;
		this->center = center;
		this->surfaceColor = surfaceColor;
		this->emissionColor = emissonColor;
		this->reflection = reflection;
		this->objectType = 1;
		this->transparency = transparency;
		vector<Triangle<T> *> mesh = ObjectParser::ParseObjFile(filePath,center );
		CreateSceneObject(1,mesh);
	}
	//position, radius, surface color, reflectivity, transparency|, emission color
	GenericObject(Vec3<T> center,T radius, Vec3<T> surfaceColor,T reflection, T transparency ,Vec3<T> emissonColor,  int type)
	{
		this->isLight = true;
		this->center = center;
		this->surfaceColor = surfaceColor;
		this->emissionColor = emissonColor;
		this->reflection = reflection;
		this->objectType = 0;
		this->radius = radius;
		this->transparency = transparency;
		vector<Triangle<T>*> triangles;

		CreateSceneObject(type, triangles);
	}

	GenericObject(Vec3<T> center,T radius, Vec3<T> surfaceColor,T reflection, T transparency ,  int type)
	{
		this->isLight = false;
		this->center = center;
		this->surfaceColor = surfaceColor;
		this->emissionColor = Vec3<T>(0);
		this->reflection = reflection;
		this->objectType = 0;
		this->radius = radius;
		this->transparency = transparency;
		vector<Triangle<T>*> triangles;

		CreateSceneObject(type, triangles);
	}

	void SetEmissionColor(Vec3<T> colorVector)
	{
		this->emissionColor = colorVector;
	}

	void SetName(string name)
	{
		this->name = name;
	}

	void SetReflection(T reflection)
	{
		this->reflection = reflection;
	}

	void SetSurfaceColor(Vec3<T> surfaceColor)
	{
		this->surfaceColor = surfaceColor;
	}

	void SetTransparency(T transparency)
	{
		this->transparency = transparency;
	}

	void SetCenter(Vec3<T> center)
	{
		this->center = center;
	}
	
	void SetEmissionLight(bool setLight)
	{

		this->emissionColor = setLight? Vec3<T>(1):Vec3<T>(0);
	}

	/*
	The mesh normal getter is not present here but tackiling it in cod efound in the trace function
	*/
	Vec3<T> GetIntersectionPointNormal(Vec3<T> &rayorig, Vec3<T> &raydir, T tnear = 0)
	{
		if(this->objectType == 0)
		{
			Vec3<T> phit = rayorig + raydir * tnear;
			return phit - this->center;
		}
		else 
		{
			return NULL;
		}
	}


	bool GetRayIntersection(Vec3<T> &rayorig, Vec3<T> &raydir, Triangle<T> * &intersectedTriangle)
	{
		return objectRep->GetIntersection(rayorig, raydir, intersectedTriangle);
	}

	Vec3<T> *GetPointIntersection(Vec3<T> &rayorig, Vec3<T> &raydir)
	{
		if(this->objectType == 0)
		{
			T t0 = 0.0f, t1 = 0.0f;
			Vec3<T> output;
			bool flag = false;
			if(this->representedSphere->intersect(rayorig, raydir, &t0, &t1))
			{
				flag = true;
				  output = rayorig + raydir * t0;
			}

			if(flag == false)
			{
				return NULL;
			}
			else
			{
				return (new Vec3<T>(output.x, output.y, output.z));
			}
			

		}
		else if(this->objectType == 1)
		{
			T t0 = 0.0f, t1 = 0.0f;
			Triangle<T> *currentTriangle = this->objectRep->GetIntersection(rayorig, raydir);
			Vec3<T> phit = currentTriangle->GetIntersectionPoint(rayorig, raydir);
			return (new Vec3<T>(phit.x, phit.y, phit.z));
		}
		else
		{
			return NULL;
		}

		return NULL;
		
	}

	bool intersect(Vec3<T> &rayorig, Vec3<T> &raydir, T &t0, T &t1)
	{
		if(this->objectType == 0)
		{
			return this->representedSphere->intersect(rayorig, raydir, &t0, &t1);
		}
		else if(this->objectType == 1)
		{
			Triangle<T> *currentTriangle = NULL;
		
			 if(this->objectRep->GetIntersection(rayorig, raydir, currentTriangle))
			 {
				return true;
			 }
			
		}
		else
		{
			return NULL;
		}
		
	}

	T GetIntersectionDistance(Vec3<T> &rayorig, Vec3<T> &raydir)
	{
		if(this->objectType == 0)
		{
			T t0 = 0.0f, t1 = 0.0f;
			this->representedSphere->intersect(rayorig, raydir, &t0, &t1);
			 return t0;
			
		}
		else if(this->objectType == 1)
		{
			T t0 = 0.0f, t1 = 0.0f;
			Triangle<T> *currentTriangle = this->objectRep->GetIntersection(rayorig, raydir);
			return currentTriangle->GetDistanceIntersection(rayorig, raydir);
		}
		else
		{
			return NULL;
		}
	}

	//Sphere(const Vec3<T> &c, const T &r, const Vec3<T> &sc, const T &refl = 0, const T &transp = 0, const Vec3<T> &ec = 0)

	void CreateSceneObject(int objectType, vector<Triangle<T>*> mesh)
	{
		switch (objectType)
		{
			case 0:
				this->representedSphere = new Sphere<T>(this->center, this->radius, this->surfaceColor, this->reflection, this->transparency);
				break;

			case 1:
				this->objectRep = new KdTree<T>(mesh);
				break;

				//light
			case 2:
				this->representedSphere = new Sphere<T>(this->center, this->radius, this->surfaceColor, this->reflection, this->transparency, this->emissionColor);
				break;

			default:
				break;
		}
	}

};

template <typename T>
class TestSphere : SceneObject<T>
{

public:
	Sphere<T> *representedSphere;

	TestSphere(Vec3<T> center,T radius, Vec3<T> surfaceColor,T reflection, T transparency ,Vec3<T> emissonColor) : SceneObject<T>(center, radius, surfaceColor, reflection, transparency, emissonColor){}

	void SetEmissionColor(Vec3<T> colorVector);
	void SetName(string name);
	void SetReflection(T reflection);
	void SetSurfaceColor(Vec3<T> surfaceColor);
	void SetTransparency(T transparency);
	void SetCenter(Vec3<T> center);
	void SetEmissionLight(bool setLight);

	virtual bool GetRayIntersection(Vec3<T> &rayorig, Vec3<T> &raydir, Vec3<T> interPoint) = 0;

	virtual  T GetIntersectionDistance(Vec3<T> &rayorig, Vec3<T> &raydir, Vec3<T> intersectionPoint) = 0;

	//Sphere(const Vec3<T> &c, const T &r, const Vec3<T> &sc, const T &refl = 0, const T &transp = 0, const Vec3<T> &ec = 0)
	virtual void CreateSceneObject() = 0;


	Vec3<T> GetIntersectionPointNormal(Vec3<T> &rayorig, Vec3<T> &raydir, T tnear)
	{
		Vec3<T> phit = rayorig + raydir * tnear;
		return phit - this->center;
	}

	Vec3<T> *GetPointIntersection(Vec3<T> &rayorig, Vec3<T> &raydir)
	{
		T t0 = 0.0f, t1 = 0.0f;
		Vec3<T> output;
		bool flag = false;
		if(this->intersect(rayorig, raydir, &t0, &t1))
		{
			flag = true;
				output = rayorig + raydir * t0;
		}

		return (new Vec3<T>(output.x, output.y, output.z));
	}


	bool intersect(Vec3<T> &rayorig, Vec3<T> &raydir, T &t0, T &t1)
	{
		return this->representedSphere->intersect(rayorig, raydir, &t0, &t1);
	}

	T GetIntersectionDistance(Vec3<T> &rayorig, Vec3<T> &raydir)
	{
		T t0 = 0.0f, t1 = 0.0f;
		this->representedSphere->intersect(rayorig, raydir, &t0, &t1);
		return t0;
	}
};

#endif